﻿/*
Copyright (c) 2009 Vratislav Cermak (vratacermak@gmail.com)
 
This file is part of TeachMe Project. See http://code.google.com/p/teach-me for more information.
 
TeachMe is an open source project. Using of TeachMe is free of charge.
It is distributed under GNU General Public License version 3 (GPLv3).
GPLv3 can be found at http://www.gnu.org/licenses/gpl.html.
 
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

using System;
using System.Linq;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using TeachMe.Application.Configuration;
using TeachMe.Application.Data;
using TeachMe.Application.Exceptions;
using TeachMe.Application.Forms;
using TeachMe.Application.Forms.Options;
using TeachMe.Application.Pexeso;
using TeachMe.Application.Properties;
using TeachMe.Application.Win32;

namespace TeachMe.Application
{
    public class ContextMenusManager
    {
        private readonly Dictionary<ContextMenuType, ContextMenuStrip> _menus = new Dictionary<ContextMenuType, ContextMenuStrip>();
        private readonly Root _root;
        private EntryDescriptor _entryDescriptor;

        public ContextMenusManager(Root root)
        {
            _root = root;
            _root.UserSettings.Language.Changed += delegate { _menus.Clear(); };
        }

        public ContextMenuStrip Get(ContextMenuType type)
        {
            if (!_menus.ContainsKey(type))
            {
                _menus.Add(type, new ContextMenuStrip());

                _menus[type].Opening += OnContextMenuOpening;
                _menus[type].Closed += OnContextMenuClosed;

                switch (type)
                {
                    case ContextMenuType.Tray:
                        AddContextMenuAction(type, Resources.MenuItemOptions, ContextMenuActionType.Options, Resources.wrench);
                        AddContextSeparator(type);
                        AddContextMenuAction(type, Resources.MenuItemPexeso, ContextMenuActionType.Pexeso, Resources.pexeso);
                        AddContextMenuAction(type, Resources.MenuItemManageDictionaries, ContextMenuActionType.ManageDictionaries, Resources.data_copy);
                        AddContextMenuAction(type, Resources.MenuItemDownloadDictionaries, ContextMenuActionType.DownloadDictionaries, Resources.download);
                        AddContextSeparator(type);
                        AddContextMenuAction(type, Resources.MenuItemCreateEntry, ContextMenuActionType.CreateEntry, Resources.note_new);
                        AddContextSeparator(type);
                        AddContextMenuAction(type, Resources.MenuItemTurboMode, ContextMenuActionType.TurboMode, true);
                        AddContextMenuAction(type, Resources.MenuItemSuspend, ContextMenuActionType.Suspend, Resources.hourglass);
                        AddContextMenuAction(type, Resources.MenuItemMaxEntriesNumbers, ContextMenuActionType.MaxEntriesNumbers, Resources.notes);
                        AddContextSeparator(type);
                        AddContextMenuAction(type, Resources.MenuItemAbout, ContextMenuActionType.About, Resources.about);
                        AddContextMenuAction(type, Resources.MenuItemExit, ContextMenuActionType.Exit, Resources.exit);
                        break;
                    case ContextMenuType.Entry:
                        AddContextMenuAction(type, Resources.MenuItemCopyEntry, ContextMenuActionType.CopyEntry, Resources.copy);
                        AddContextSeparator(type);
                        AddContextMenuAction(type, Resources.MenuItemEditEntry, ContextMenuActionType.EditEntry, Resources.note_edit);
                        AddContextMenuAction(type, Resources.MenuItemRemoveEntry, ContextMenuActionType.RemoveEntry, Resources.note_delete);
                        AddContextSeparator(type);
                        AddContextMenuAction(type, Resources.MenuItemChangeDirection, ContextMenuActionType.ChangeDirection, Resources.change_direction);
                        AddContextMenuAction(type, Resources.MenuItemEditDictionary, ContextMenuActionType.EditDictionary, Resources.data_edit);
                        AddContextMenuAction(type, Resources.MenuItemDisableDictionary, ContextMenuActionType.DisableDictionary, Resources.disable_dict);
                        break;
                    default:
                        throw new ArgumentOutOfRangeException("type");
                }
            }

            return _menus[type];
        }

        static void OnContextMenuClosed(object sender, ToolStripDropDownClosedEventArgs e)
        {
            // do not enable hooks again after context menu was closed
            MouseHook.Instance.Enabled = false;
        }

        private ToolStripMenuItem AddContextMenuAction(ContextMenuType type, string text, ContextMenuActionType actionType, Image image = null)
        {
            ToolStripMenuItem item = new ToolStripMenuItem(text);
            item.Click += OnActionExecute;
            item.Tag = actionType;
            if (image != null)
            {
                item.Image = image;
            }
            _menus[type].Items.Add(item);

            return item;
        }

        private void AddContextMenuAction(ContextMenuType type, string text, ContextMenuActionType actionType, Image image, ToolStripItemCollection parent)
        {
            ToolStripMenuItem item = new ToolStripMenuItem(text);
            item.Click += OnActionExecute;
            item.Tag = actionType;
            if (image != null)
            {
                item.Image = image;
            }
            parent.Add(item);
        }

        private void AddContextMenuAction(ContextMenuType type, string text, ContextMenuActionType actionType, bool hasCheckBox)
        {
            ToolStripMenuItem item = new ToolStripMenuItem(text);
            item.Click += OnActionExecute;
            item.Tag = actionType;
            item.CheckOnClick = hasCheckBox;
            _menus[type].Items.Add(item);
        }

        private void AddContextSeparator(ContextMenuType type)
        {
            _menus[type].Items.Add(new ToolStripSeparator());
        }

        void OnContextMenuOpening(object sender, System.ComponentModel.CancelEventArgs e)
        {
            MouseHook.Instance.Enabled = false;

            ContextMenuStrip menu = sender as ContextMenuStrip;
            if (menu == null)
            {
                throw new InvalidOperationException();
            }

            Logging.Debug(GetType(), "Number of active forms: {0}", _root.FormsManager.Count);

            foreach (object item in menu.Items)
            {
                ToolStripMenuItem menuItem = item as ToolStripMenuItem;

                if (menuItem == null)
                {
                    continue;
                }

                switch ((ContextMenuActionType) menuItem.Tag)
                {
                    case ContextMenuActionType.ManageDictionaries:
                        menuItem.Enabled = _root.FormsManager.Count == 0 || _root.FormsManager.Get<DictionariesManagerForm>() != null;
                        break;
                    case ContextMenuActionType.DownloadDictionaries:
                        menuItem.Enabled = _root.FormsManager.Count == 0 || _root.FormsManager.Get<OnlineDictsForm>() != null;
                        break;
                    case ContextMenuActionType.CopyEntry:
                        menuItem.Enabled = true;
                        break;
                    case ContextMenuActionType.EditEntry:
                        menuItem.Enabled = _root.FormsManager.Count == 0;
                        break;
                    case ContextMenuActionType.EditDictionary:
                        menuItem.Enabled = _root.FormsManager.Count == 0;
                        break;
                    case ContextMenuActionType.RemoveEntry:
                        menuItem.Enabled = _root.FormsManager.Get<AdvancedEntryEditorForm>() == null && _root.FormsManager.Get<EntriesEditorForm>() == null;
                        break;
                    case ContextMenuActionType.CreateEntry:
                        menuItem.Enabled = _root.FormsManager.Count == 0;
                        break;
                    case ContextMenuActionType.Options:
                        menuItem.Enabled = _root.FormsManager.Count == 0 || _root.FormsManager.Get<OptionsForm>() != null;
                        break;
                    case ContextMenuActionType.TurboMode:
                        menuItem.Enabled = _root.UserSettings.NewEntryMode.Value != NewEntryMode.OnDemand && !_root.Suspender.IsSuspended;
                        menuItem.Checked = _root.TurboMode && menuItem.Enabled;
                        break;
                    case ContextMenuActionType.Exit:
                        break;
                    case ContextMenuActionType.DisplayEntry:
                        break;
                    case ContextMenuActionType.CloseEntry:
                        break;
                    case ContextMenuActionType.CloseEntries:
                        break;
                    case ContextMenuActionType.Suspend:
                        menuItem.Enabled = _root.UserSettings.NewEntryMode.Value != NewEntryMode.OnDemand;
                        PopulateSuspendMenuItem(menuItem);
                        break;
                    case ContextMenuActionType.MaxEntriesNumbers:
                        PopulateMaxEntriesMenuItem(menuItem);
                        break;
                    case ContextMenuActionType.Pexeso:
                        menuItem.Enabled = _root.FormsManager.Count == 0 || _root.FormsManager.Get<PexesoForm>() != null;
                        break;
                    case ContextMenuActionType.About:
                        menuItem.Enabled = _root.FormsManager.Count == 0 || _root.FormsManager.Get<AboutForm>() != null;
                        break;
                    case ContextMenuActionType.DisableDictionary:
                        break;
                    case ContextMenuActionType.ChangeDirection:
                        menuItem.Enabled = !_entryDescriptor.Dictionary.Entries.OneWay;
                        PopulateChangeDirectionMenuItem(menuItem);
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
        }

        private void DownloadDictionaries()
        {
            OnlineDictsManager manager = new OnlineDictsManager(_root, null);

            manager.GetLoadedDictionary += OnGetLoadedDictionary;
            manager.GetLoadedDictionaryId += OnGetLoadedDictionaryId;
            manager.IsDictionaryLoaded += OnIsDictionaryLoaded;
            manager.AddDictionaryUI += OnAddDictionaryUI;
            manager.RefreshDictionaryUI += OnRefreshDictionaryUI;

            manager.AddOnlineDicts();
        }

        void OnRefreshDictionaryUI(object sender, Events.InputEventArgs<Dictionary> e)
        {
            _root.DisplayEntry();
        }

        void OnAddDictionaryUI(object sender, Events.InputEventArgs<Dictionary> e)
        {
            _root.DisplayEntry();
        }

        void OnIsDictionaryLoaded(object sender, Events.InputOutputEventArgs<string, bool> e)
        {
            e.Output = GetDictionary(e.Input) != null;
        }

        void OnGetLoadedDictionaryId(object sender, Events.InputOutputEventArgs<string, Guid> e)
        {
            e.Output = GetDictionary(e.Input) != null ? GetDictionary(e.Input).Entries.Id : Guid.Empty;
        }

        void OnGetLoadedDictionary(object sender, Events.InputOutputEventArgs<string, Dictionary> e)
        {
            e.Output = GetDictionary(e.Input);
        }

        private Dictionary GetDictionary(string path)
        {
            foreach (Dictionary dictionary in _root.Dictionaries)
            {
                if (String.Compare(dictionary.UserData.Path, path, true) == 0)
                {
                    return dictionary;
                }
            }

            return null;
        }

        private void PopulateChangeDirectionMenuItem(ToolStripDropDownItem menuItem)
        {
            menuItem.DropDownItems.Clear();

            if (_entryDescriptor.Dictionary.Entries.OneWay)
            {
                return;
            }

            var from = _entryDescriptor.Dictionary.Entries.From;
            var to = _entryDescriptor.Dictionary.Entries.To;
            var translatedFrom = Singleton<Languages>.Instance.GetByCode(from) != null ? Singleton<Languages>.Instance.GetByCode(from).Localized : from;
            var translatedTo = Singleton<Languages>.Instance.GetByCode(to) != null ? Singleton<Languages>.Instance.GetByCode(to).Localized : to;

            var mi = new ToolStripMenuItem { Text = String.Format(Resources.OneDirection, translatedFrom, translatedTo), Checked = _entryDescriptor.Dictionary.UserData.EntryTextDirection == EntryTextDirection.FromTo };
            mi.Click += delegate
                            {
                                using (_root.UserSettings.Edit())
                                {
                                    _entryDescriptor.Dictionary.UpdateUserSettings(EntryTextDirection.FromTo);
                                }
                            };
            menuItem.DropDownItems.Add(mi);

            mi = new ToolStripMenuItem { Text = String.Format(Resources.OneDirection, translatedTo, translatedFrom), Checked = _entryDescriptor.Dictionary.UserData.EntryTextDirection == EntryTextDirection.ToFrom };
            mi.Click += delegate
                            {
                                using (_root.UserSettings.Edit())
                                {
                                    _entryDescriptor.Dictionary.UpdateUserSettings(EntryTextDirection.ToFrom);
                                }
                            };
            menuItem.DropDownItems.Add(mi);

            mi = new ToolStripMenuItem { Text = String.Format(Resources.BothDirections, translatedFrom, translatedTo), Checked = _entryDescriptor.Dictionary.UserData.EntryTextDirection == EntryTextDirection.Both };
            mi.Click += delegate
                            {
                                using (_root.UserSettings.Edit())
                                {
                                    _entryDescriptor.Dictionary.UpdateUserSettings(EntryTextDirection.Both);
                                }
                            };
            menuItem.DropDownItems.Add(mi);
        }

        private void PopulateMaxEntriesMenuItem(ToolStripDropDownItem menuItem)
        {
            menuItem.DropDownItems.Clear();

            foreach (int maxEntriesNumber in Utilities.ParseToNumbers(_root.UserSettings.MaxEntriesNumbers.Value).Where(x => x <= UserSettings.MaxEntriesCount))
            {
                int number = maxEntriesNumber;

                ToolStripMenuItem item = new ToolStripMenuItem(number.ToString());
                item.Click += delegate
                                  {
                                      using (_root.UserSettings.Edit())
                                      {
                                          _root.UserSettings.MaxEntriesNumber.Value = number;
                                      }
                                  };
                if (_root.UserSettings.MaxEntriesNumber.Value == number)
                {
                    item.Checked = true;
                }

                menuItem.DropDownItems.Add(item);
            }
        }

        private void PopulateSuspendMenuItem(ToolStripDropDownItem menuItem)
        {
            menuItem.DropDownItems.Clear();

            if (menuItem.Enabled)
            {
                ToolStripMenuItem item = new ToolStripMenuItem(Resources.MenuItemResume);
                item.Click += delegate { _root.Suspender.Resume(); };
                item.Enabled = _root.Suspender.IsSuspendedByInterval;

                menuItem.DropDownItems.Add(item);
                menuItem.DropDownItems.Add(new ToolStripSeparator());

                foreach (int suspendInterval in Utilities.ParseToNumbers(_root.UserSettings.SuspendIntervals.Value))
                {
                    int interval = suspendInterval;

                    item = new ToolStripMenuItem(String.Format(Resources.SuspendInterval, suspendInterval));
                    item.Click += delegate { _root.Suspender.Suspend(interval); };
                    if (_root.Suspender.IsSuspended)
                    {
                        item.Enabled = false;
                    }

                    menuItem.DropDownItems.Add(item);
                }
            }
        }

        void OnActionExecute(object sender, EventArgs e)
        {
            ToolStripMenuItem menuItem = sender as ToolStripMenuItem;
            if (menuItem == null)
            {
                throw new InvalidOperationException();
            }

            switch ((ContextMenuActionType)menuItem.Tag)
            {
                case ContextMenuActionType.EditEntry:
                    EditEntry();
                    break;
                case ContextMenuActionType.EditDictionary:
                    EditDictionary();
                    break;
                case ContextMenuActionType.RemoveEntry:
                    try
                    {
                        _entryDescriptor.Dictionary.UpdateEntry(EditedEntryDescriptor.CreateRemoved(_entryDescriptor.EntryId));
                    }
                    catch (DictionaryException exception)
                    {
                        Logging.Error(GetType(), exception, "Entry could not be removed.");

                        MessageBox.Show(Resources.DictionarySaveError, Resources.MessageBoxCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    break;
                case ContextMenuActionType.CreateEntry:
                    _root.CreateEntry();
                    break;
                case ContextMenuActionType.Options:
                    _root.FormsManager.ShowOptionsForm();
                    break;
                case ContextMenuActionType.TurboMode:
                    _root.TurboMode = menuItem.Checked;
                    break;
                case ContextMenuActionType.Exit:
                    System.Windows.Forms.Application.Exit();
                    break;
                case ContextMenuActionType.DisplayEntry:
                    _root.DisplayEntry();
                    break;
                case ContextMenuActionType.CloseEntry:
                    _root.DisplayManager.CloseEntry(_entryDescriptor);
                    break;
                case ContextMenuActionType.CloseEntries:
                    _root.DisplayManager.CloseEntries();
                    break;
                case ContextMenuActionType.CopyEntry:
                    CopyEntry();
                    break;
                case ContextMenuActionType.Suspend:
                    break;
                case ContextMenuActionType.MaxEntriesNumbers:
                    break;
                case ContextMenuActionType.About:
                    _root.FormsManager.ShowAboutForm();
                    break;
                    case ContextMenuActionType.Pexeso:
                    _root.FormsManager.ShowPexesoForm();
                    break;
                case ContextMenuActionType.DownloadDictionaries:
                    DownloadDictionaries();
                    break;
                case ContextMenuActionType.ManageDictionaries:
                    _root.FormsManager.ShowDictionariesManager();
                    break;
                case ContextMenuActionType.DisableDictionary:
                    if (_root.EnabledDictionariesCount <= 1 && MessageBox.Show(Resources.DisableLastEnabledDict, Resources.MessageBoxCaption, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
                    {
                        break;
                    }
                    using (_root.UserSettings.Edit())
                    {
                        _entryDescriptor.Dictionary.UpdateUserSettings(false);
                    }
                    break;
                case ContextMenuActionType.ChangeDirection:
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        private void CopyEntry()
        {
            ClipboardEntries entries = new ClipboardEntries
                                           {
                                               From = _entryDescriptor.Dictionary.Entries.From,
                                               To = _entryDescriptor.Dictionary.Entries.To,
                                               Items = new List<ClipboardEntries.ClipboardEntry>()
                                           };

            ClipboardEntries.ClipboardEntry entry = new ClipboardEntries.ClipboardEntry
                                                        {
                                                            From = _entryDescriptor.Entry.From,
                                                            To = _entryDescriptor.Entry.To,
                                                            Example = _entryDescriptor.Example
                                                        };

            entries.Items.Add(entry);

            entries.Copy();
        }

        private void EditDictionary()
        {
            EntriesEditorForm form = new EntriesEditorForm(_root, _root.Dictionaries, _entryDescriptor.Dictionary);
            _root.FormsManager.Add(form);
            form.Show();
            form.Closed += delegate
                                            {
                                                _root.FormsManager.Remove(form);
                                            };
        }

        private void EditEntry()
        {
            Entry entry = _entryDescriptor.Dictionary.Entries.Items[_entryDescriptor.EntryId];

            List<EditedEntryDescriptor> entries = new List<EditedEntryDescriptor>();
            entries.Add(EditedEntryDescriptor.Create(entry, _entryDescriptor.Dictionary.GetEntryPriority(entry.Id)));

            AdvancedEntryEditorForm form = new AdvancedEntryEditorForm(_root, entries, _entryDescriptor.Dictionary);
            _root.FormsManager.Add(form);
            form.Show();
            form.Closed += delegate
                               {
                                   _root.FormsManager.Remove(form);
                               };
        }

        public void SetCurrentEntryDescriptor(EntryDescriptor descriptor)
        {
            _entryDescriptor = descriptor;
        }
    }
}
